#pragma once
#ifndef MODELFORMATION_H
#define MODELFORMATION_H

#if defined(Viewer3DAPI_ModelPrimitive)
#  define Viewer3DAPI_EXPORT Q_DECL_EXPORT
#else
#  define Viewer3DAPI_EXPORT
#endif

#include <QVector3D>
#include <QVector>
#include <limits>
#include <QSet>


bool Viewer3DAPI_EXPORT floatEquals(float a, float b, float epsilon = std::numeric_limits<float>::epsilon());
bool Viewer3DAPI_EXPORT IsEqual(float f1, float f2, int absDelta);

constexpr float vertexEpsilon = 1e-5f;
constexpr int absDelta = 1e-5f / 1.19e-7f;

struct Viewer3DAPI_EXPORT Material {
    QString name;
    QVector3D ambient;
    QVector3D diffuse;
    QVector3D specular;
    float shininess;

    QString diffuseTexturePath;
    QString specularTexturePath;
    QString normalTexturePath;

    QByteArray embeddedDiffuseTexture;
    QByteArray embeddedSpecularTexture;
    QByteArray embeddedNormalTexture;

    unsigned int diffuseTextureID;
    unsigned int specularTextureID;
    unsigned int normalTextureID;

    Material()
        : ambient(0.2f, 0.2f, 0.2f)
        , diffuse(0.8f, 0.8f, 0.8f)
        , specular(1.0f, 1.0f, 1.0f)
        , shininess(32.0f)
        , diffuseTextureID(0)
        , specularTextureID(0)
        , normalTextureID(0)
    {}
};

struct Viewer3DAPI_EXPORT Light {
    enum Type { Directional, Point, Spot } type;

    QVector3D color = QVector3D(1.0f, 1.0f, 1.0f);
    QVector3D position;
    QVector3D direction;

    float constant = 1.0f;
    float linear = 0.0f;
    float quadratic = 0.0f;

    float cutOff = 12.5f;
    float outerCutOff = 15.0f;
};

struct Viewer3DAPI_EXPORT Vertex
{
    QVector3D pos;
    QVector3D norm;
    QVector4D color;
    QVector2D texCoord;
    QVector3D tangent;
    QVector3D bitangent;
    std::shared_ptr<Vertex> sptr_endPointa = nullptr, sptr_endPointb = nullptr;

    unsigned int materialIndex;

    bool operator==(const Vertex &other) const {
        return IsEqual(pos.x(), other.pos.x(), absDelta) &&
               IsEqual(pos.y(), other.pos.y(), absDelta) &&
               IsEqual(pos.z(), other.pos.z(), absDelta);
    }

    bool operator!=(const Vertex &other) const {
        return !(*this == other);
    }

    bool operator<(const Vertex &other) const {
        if (!IsEqual(pos.x(), other.pos.x(), absDelta)) return pos.x() < other.pos.x();
        if (!IsEqual(pos.y(), other.pos.y(), absDelta)) return pos.y() < other.pos.y();
        return pos.z() < other.pos.z();
    }

    uint hash() const {
        uint hashValue = qHash(static_cast<int>(pos.x() * 100000));
        hashValue ^= qHash(static_cast<int>(pos.y() * 100000)) + 0x9e3779b9 + (hashValue << 6) + (hashValue >> 2);
        hashValue ^= qHash(static_cast<int>(pos.z() * 100000)) + 0x9e3779b9 + (hashValue << 6) + (hashValue >> 2);
        return hashValue;
    }
};

inline uint qHash(const Vertex &vertex, uint seed = 0) {
    return vertex.hash() ^ seed;
}

struct Viewer3DAPI_EXPORT Edge
{
    Vertex v1, v2;
    QVector3D norm;

    bool operator==(const Edge &other) const {
        return (v1 == other.v1 && v2 == other.v2) || (v1 == other.v2 && v2 == other.v1);
    }

    bool operator<(const Edge &other) const {
        if (v1 != other.v1) return v1 < other.v1;
        return v2 < other.v2;
    }

    uint hash() const {
        uint hash1 = v1.hash();
        uint hash2 = v2.hash();
        return qMin(hash1, hash2) ^ qMax(hash1, hash2);
    }
};

inline uint qHash(const Edge &edge, uint seed = 0) {
    return edge.hash() ^ seed;
}

struct Viewer3DAPI_EXPORT Triangle
{
    QVector3D v1, v2, v3;
    QVector4D v1_color{1.0f, 1.0f, 1.0f, 1.0f}, v2_color{1.0f, 1.0f, 1.0f, 1.0f}, v3_color{1.0f, 1.0f, 1.0f, 1.0f};
    QVector3D normal;
    quint16 info;

    Triangle& operator=(const Triangle& other) {
        if (this != &other) {
            v1 = other.v1;
            v2 = other.v2;
            v3 = other.v3;
            v1_color = other.v1_color;
            v2_color = other.v2_color;
            v3_color = other.v3_color;
            normal = other.normal;
            info = other.info;
        }
        return *this;
    }

    bool operator==(const Triangle &other) const {
        return v1 == other.v1 && v2 == other.v2 && v3 == other.v3;
    }
};

struct Viewer3DAPI_EXPORT Layer
{
    QVector<Vertex> points;
};

#endif // MODELFORMATION_H
